package corbaEventService;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import org.omg.CORBA.Any;
import org.omg.CORBA.BooleanHolder;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;

import cosEventChannelAdmin.AlreadyConnected;
import cosEventChannelAdmin.ConsumerAdmin;
import cosEventChannelAdmin.ConsumerAdminHelper;
import cosEventChannelAdmin.EventChannel;
import cosEventChannelAdmin.EventChannelHelper;
import cosEventChannelAdmin.EventChannelPOA;
import cosEventChannelAdmin.SupplierAdmin;
import cosEventChannelAdmin.SupplierAdminHelper;
import cosEventComm.Disconnected;
import cosEventComm.PullSupplier;
import cosEventComm.PushConsumer;


public class EventChannelImpl extends EventChannelPOA{
	private Hashtable<Integer,Any> eventList;
	private Hashtable<String,Integer> clientCount;
	private Integer channelCountTop;
	private Integer channelCountBottom;
	private SupplierAdmin supplierAdmin;
	private ConsumerAdmin consumerAdmin;
	private ArrayList<String> serverCount;
	
	public EventChannelImpl(){		
		eventList = new Hashtable<Integer,Any>();
		clientCount = new Hashtable<String,Integer>();
		serverCount = new ArrayList<String>();
		channelCountTop = 0;
		channelCountBottom = 0;
	}	

	@Override
	public ConsumerAdmin for_consumers() {
		initConsumerAdmin();
		return consumerAdmin;
	}

	@Override
	public SupplierAdmin for_suppliers() {		
		initSupplierAdmin();
		return supplierAdmin;
	}

	@Override
	public void destroy() {
		ORB orb = ORB.init();
		eventList.clear();
		clientCount.clear();
		serverCount.clear();
		orb.disconnect(consumerAdmin);
		orb.disconnect(supplierAdmin);
		
		Object ref = null;
		try {
			ref = _poa().servant_to_reference(this);
		} catch (ServantNotActive e) {				
			e.printStackTrace();
		} catch (WrongPolicy e) {				
			e.printStackTrace();
		}
		EventChannel channel = EventChannelHelper.narrow(ref);		
		orb.disconnect(channel);
	}

	@Override
	public void push(Any data) throws Disconnected {
		channelCountTop = channelCountTop +1;
		eventList.put(channelCountTop,data);		
	}

	
	public Any try_pull(String clientID, BooleanHolder has_event) throws Disconnected{
		System.out.println("try");
		if(!clientCount.containsKey(clientID)){
			throw new Disconnected();
		}
		
		if(clientCount.get(clientID) > channelCountTop){
			System.out.println("1");
			has_event.value = false;
			return ORB.init().create_any();
		}
		
		System.out.println("2");
		Any event = eventList.get(clientCount.get(clientID));
		clientCount.put(clientID, clientCount.get(clientID)+1);
		checkInvalidEvents();
		has_event.value = true;
		return event;
	}
	
	private void checkInvalidEvents(){
		Enumeration<Integer> elements = clientCount.elements();
		Integer min = elements.nextElement();
		while(elements.hasMoreElements()){
			Integer current = elements.nextElement();
			if(current < min){
				min = current;
			}
			for(int i = channelCountBottom; i < min;i++){
				eventList.remove(i);
			}
			channelCountBottom = min -1;
		}
	}

	private void initSupplierAdmin(){
		if(supplierAdmin == null){
			Object ref = null;
			try {
				ref = _poa().servant_to_reference(this);
			} catch (ServantNotActive e) {				
				e.printStackTrace();
			} catch (WrongPolicy e) {				
				e.printStackTrace();
			}
			EventChannel channel = EventChannelHelper.narrow(ref);
			SupplierAdminImpl supplierAdminImpl = new SupplierAdminImpl(channel);
			try {
				ref = _poa().servant_to_reference(supplierAdminImpl);
			} catch (ServantNotActive e) {				
				e.printStackTrace();
			} catch (WrongPolicy e) {				
				e.printStackTrace();
			}
			supplierAdmin = SupplierAdminHelper.narrow(ref);
		}
	}
	
	private void initConsumerAdmin(){
		if(consumerAdmin == null){
			Object ref = null;
			try {
				ref = _poa().servant_to_reference(this);
			} catch (ServantNotActive e) {				
				e.printStackTrace();
			} catch (WrongPolicy e) {				
				e.printStackTrace();
			}
			EventChannel channel = EventChannelHelper.narrow(ref);
			ConsumerAdminImpl consumerAdminImpl = new ConsumerAdminImpl(channel);
			try {
				ref = _poa().servant_to_reference(consumerAdminImpl);
			} catch (ServantNotActive e) {				
				e.printStackTrace();
			} catch (WrongPolicy e) {				
				e.printStackTrace();
			}
			consumerAdmin = ConsumerAdminHelper.narrow(ref);			
		}
	}

	@Override
	public void connect_push_consumer(PushConsumer push_consumer)
			throws AlreadyConnected {
		String supplierId = push_consumer.getID();
		if(serverCount.contains(supplierId)){
			throw new AlreadyConnected();
		}
		serverCount.add(supplierId);
		
	}

	@Override
	public void connect_pull_supplier(PullSupplier pull_supplier)
			throws AlreadyConnected {
		String consumerId = pull_supplier.getID();

		if(clientCount.containsKey(consumerId)){
			throw new AlreadyConnected();
		}
		clientCount.put(consumerId, channelCountTop+1);
		System.out.println(consumerId);
		System.out.println("top: "+channelCountTop);
	}

	@Override
	public void disconnect_push_consumer(PushConsumer push_consumer) {
		String supplierId = push_consumer.getID();
		serverCount.remove(supplierId);
		
	}

	@Override
	public void disconnect_pull_supplier(PullSupplier pull_supplier) {
		String consumerId = pull_supplier.getID();
		clientCount.remove(consumerId);
	}


}
